package com.lgy.httpmocktool.util;

import com.alibaba.fastjson.JSONObject;
import com.lgy.httpmocktool.config.PathConfig;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.springframework.util.DigestUtils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class GroovyUtil {

    private static Map<String, GroovyObject> groovyObjectCache = new ConcurrentHashMap<>();

    private static int maxSize = 500;

    /**
     * 根据配置的规则获取Groovy脚本的名称
     * @param reqBody
     * @return
     */
    public static String getGroovyName(JSONObject reqBody) {
        log.info("当前groovyObjectCache数量为：{}", groovyObjectCache.size());
        String groovyName = executeDefault("RoutingRules", reqBody);
        log.info("当前groovyName为：" + groovyName);
        if (groovyObjectCache.size() > maxSize) {
            groovyObjectCache.clear();
            log.info("缓存区的数量超过阈值，清空缓存，当前groovyObjectCache数量为：{}", groovyObjectCache.size());
        }
        return groovyName;
    }

    public static <T> T executeDefault(String groovyName, JSONObject reqBody) {
        Object[] params = {reqBody};
        String groovyPath = new File(PathConfig.getGroovy() + File.separator + groovyName + ".groovy").getPath();
        log.debug("groovyPath : {}", groovyPath);
        return execute(groovyPath, "execute" , params);
    }

    /**
     * 执行脚本
     * @param groovyPath 脚本路径
     * @param methodName 执行的方法
     * @param params 执行的参数列表
     * @return 返回执行结果
     * @param <T>
     */
    public static <T> T execute(String groovyPath, String methodName, Object[] params) {
        T respBody = null;
        try {
            Path path = Paths.get(groovyPath);
            byte[] reads = Files.readAllBytes(path);
            GroovyObject groovyObject = buildScript(new String(reads));
            respBody = (T) groovyObject.invokeMethod(methodName, params);
        } catch (Exception e) {
            log.error("Groovy文件执行失败！！！, groovyPath : {} ", groovyPath);
        }
        return respBody;
    }

    /**
     * 热加载脚本
     * @param script
     * @return
     */
    public static GroovyObject buildScript(String script) {
        if (StringUtils.isEmpty(script)) {
            throw new RuntimeException("script is empty");
        }
        //比较文件md5值，如果文件未变化则返回原脚本对象
        String cacheKey = DigestUtils.md5DigestAsHex(script.getBytes());
        if (groovyObjectCache.containsKey(cacheKey)) {
            log.debug("groovyObjectCache hit");
            return groovyObjectCache.get(cacheKey);
        }

        GroovyClassLoader classLoader = new GroovyClassLoader();
        try {
            Class<?> groovyClass = classLoader.parseClass(script);
            GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
            classLoader.clearCache();

            groovyObjectCache.put(cacheKey, groovyObject);
            log.info("groovy buildScript success: {}", groovyObject);
            return groovyObject;
        } catch (Exception e) {
            throw new RuntimeException("buildScript error", e);
        } finally {
            try {
                classLoader.close();
            } catch (IOException e) {
                log.error("close GroovyClassLoader error", e);
            }
        }
    }

}
